﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using BoatNGoat.Host;
using BoatNGoat.Input;
using BoatNGoat.Input.ImpulseLayer;
using BoatNGoat.Logic.Map;
using BoatNGoat.Server;
using SharpDX;
using SharpDX.Toolkit;

namespace BoatNGoat
{

    /// <summary>
    /// Local simulation with predition for mob movement
    /// </summary>
    public class LocalSimulation : IUpdateable, IDisposable
    {
        public Player Player { get; set; }
        private readonly ConcurrentDictionary<int, IMobile> _localGoats = new ConcurrentDictionary<int, IMobile>();
        private readonly BoatNGoatHost _server;
        private readonly bool _enablePrediction;
        private readonly Timer _timer;
        private readonly IMap _map;

        public LocalSimulation(BoatNGoatHost server, int networkRequestsPerSecond, bool enablePrediction)
        {
            _server = server;
            _enablePrediction = enablePrediction;
            // simulated network updated
            int interval = 1000 / networkRequestsPerSecond;
            _timer = new Timer(NetworkUpdate, null, interval, interval);
            _map = new LocalMap(server.GetMap().Tiles);

            // Code temporarily here because, shit, I didn't know where else to put it.
            Action a1 = (() => Player.Velocity = new Vector2(Player.Velocity.X, Player.MoveSpeed));
            Action a2 = (() => Player.Velocity = new Vector2(-Player.MoveSpeed, Player.Velocity.Y));
            Action a3 = (() => Player.Velocity = new Vector2(Player.Velocity.X, -Player.MoveSpeed));
            Action a4 = (() => Player.Velocity = new Vector2(Player.MoveSpeed, Player.Velocity.Y)); 
            Action a5 = (() => Player.MoveSpeed = 30);

            InputManager.RegisterImpulseAction("PlayerMoveUp", a1);
            InputManager.RegisterImpulseAction("PlayerMoveLeft", a2);
            InputManager.RegisterImpulseAction("PlayerMoveDown", a3);
            InputManager.RegisterImpulseAction("PlayerMoveRight", a4);
            InputManager.RegisterImpulseAction("PlayerSprintOn", a5);

            Action a6 = (() => Player.Velocity = new Vector2(Player.Velocity.X, 0));
            Action a7 = (() => Player.Velocity = new Vector2(0, Player.Velocity.Y));
            Action a8 = (() => Player.Velocity = new Vector2(Player.Velocity.X, 0));
            Action a9 = (() => Player.Velocity = new Vector2(0, Player.Velocity.Y));
            Action a10 = (() => Player.MoveSpeed = 1);

            InputManager.RegisterImpulseAction("PlayerMoveUpStop", a6);
            InputManager.RegisterImpulseAction("PlayerMoveLeftStop", a7);
            InputManager.RegisterImpulseAction("PlayerMoveDownStop", a8);
            InputManager.RegisterImpulseAction("PlayerMoveRightStop", a9);
            InputManager.RegisterImpulseAction("PlayerSprintOff", a10);
        }

        private void NetworkUpdate(object obj)
        {
            IEnumerable<IMobile> mobs = _server.GetGoats();
            foreach (var mobile in mobs)
            {
                _localGoats[mobile.EntityId] = mobile;
            }
        }

        public IEnumerable<IMobile> GetGoats()
        {
            return _localGoats.Values;
        }

        public Player GetLocalPlayer()
        {
            return Player;
        }

        public void Update(GameTime gameTime)
        {
            if (_enablePrediction)
            {
                float time = (float) gameTime.ElapsedGameTime.TotalSeconds;
                foreach (var localGoat in _localGoats.Values)
                {
                    localGoat.Position += localGoat.Velocity*time;
                }
            }
        }

        public IMap GetMap()
        {
            return _map;
        }

        public void Dispose()
        {
            _timer.Dispose();
        }
    }
}
